<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>智能媒体整理工具</title>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <style>
        .el-card {
            margin-bottom: 20px;
        }

        .step-content {
            margin-top: 20px;
            padding: 20px;
            border: 1px solid #e6e6e6;
            border-radius: 4px;
        }

        .hidden {
            display: none;
        }

        /* 重复文件组卡片样式调整 */
        .group-card {
            margin-bottom: 15px;
            width: 32%;
            /* 设置卡片宽度为32%，留出间距 */
            display: inline-block;
            /* 使卡片横向排列 */
            margin-right: 1%;
            /* 右侧间距 */
            vertical-align: top;
            /* 顶部对齐 */
            box-sizing: border-box;
            /* 确保内边距和边框不影响宽度计算 */
            min-width: 550px;
        }

        /* 每行第3个卡片去除右侧间距 */
        .group-card:nth-child(3n) {
            margin-right: 0;
        }

        .file-item {
            display: flex;
            align-items: center;
            padding: 10px;
            border-bottom: 1px dashed #e6e6e6;
            flex-wrap: wrap;
            /* 允许文件信息换行 */
        }

        .file-item:last-child {
            border-bottom: none;
        }

        .quality-tag {
            margin-left: 10px;
        }

        .file-path {
            flex: 1;
            /* margin: 0 10px; */
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            flex-basis: 100%; /* 文件路径占满一行 */
            direction: rtl;
            text-align: left;
        }        
        .file-path-text {
            display: inline-block;
            /* width: 100%; */
            overflow: hidden;
            text-overflow: ellipsis;
            direction: ltr;
        }

        /* 媒体预览相关样式 */
        .image-preview,
        .video-preview {
            width: 100px;
            height: 100px;
            object-fit: cover;
            border-radius: 4px;
            cursor: pointer;
            transition: transform 0.2s;
            border: 2px solid transparent;
        }

        .image-preview:hover,
        .video-preview:hover {
            transform: scale(1.05);
        }

        .image-preview.selected,
        .video-preview.selected {
            border-color: #409EFF;
        }

        /* 视频文件标识样式 */
        .video-placeholder {
            width: 100px;
            height: 100px;
            background-color: #f5f5f5;
            display: flex;
            justify-content: center;
            align-items: center;
            border-radius: 4px;
            color: #909399;
            font-size: 12px;
            text-align: center;
        }

        /* 通用媒体预览弹窗样式 */
        .media-preview-modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.9);
            z-index: 1000;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .media-preview-modal.active {
            display: flex;
        }

        .media-preview-content {
            position: relative;
            max-width: 90%;
            max-height: 90%;
        }

        .preview-media {
            max-width: 100%;
            max-height: 80vh;
            border: 5px solid white;
        }

        .close-preview {
            position: absolute;
            top: -40px;
            right: 0;
            color: white;
            font-size: 30px;
            cursor: pointer;
            z-index: 1001;
        }

        /* 文件类型图标样式 */
        .file-type-icon {
            margin-right: 0;
        }

        /* 功能选择卡片样式 */
        .box-card {
            display: inline-block;
            margin: 15px;
            width: 300px;
            vertical-align: top;
        }
        .el-radio-group {
            font-size: unset !important;
        }
        .radio-class { 
            width: 100%;
        }
    </style>
</head>

<body>
    <div id="app" class="container" style="margin: 20px;">
        <el-card>
            <h2>智能媒体整理工具</h2>
            <el-steps :active="activeStep" finish-status="success" style="margin-top: 30px;">
                <el-step title="选择功能"></el-step>
                <el-step title="参数设置"></el-step>
                <el-step title="执行结果"></el-step>
            </el-steps>

            <!-- 步骤1: 选择功能 -->
            <div v-if="activeStep === 0" class="step-content">
                <el-radio-group v-model="selectedFunction" class="radio-group">
                    <el-card class="box-card">
                        <div slot="header" class="clearfix">
                            <h3>功能一: 扫描文件并分析</h3>
                        </div>
                        <div class="text item">
                            <p>遍历指定目录，分析照片和视频文件</p>
                            <p>提取元数据并计算质量评分</p>
                            <p>结果存储到数据库中</p>
                        </div>
                        <el-radio label="scan" border class="radio-class"></el-radio>
                    </el-card>

                    <el-card class="box-card">
                        <div slot="header" class="clearfix">
                            <h3>功能二: 查找并删除重复文件</h3>
                        </div>
                        <div class="text item">
                            <p>按哈希值或文件名查找重复</p>
                            <p>自动标记质量较低的重复文件</p>
                            <p>支持图片预览和视频播放</p>
                        </div>
                        <el-radio label="duplicate" border class="radio-class"></el-radio>
                    </el-card>

                    <el-card class="box-card">
                        <div slot="header" class="clearfix">
                            <h3>功能三: 整理文件到目录</h3>
                        </div>
                        <div class="text item">
                            <p>按拍摄时间整理文件</p>
                            <p>创建年/年月层级目录结构</p>
                            <p>智能重命名避免冲突</p>
                        </div>
                        <el-radio label="organize" border class="radio-class"></el-radio>
                    </el-card>
                </el-radio-group>
                <div style="text-align: center; margin-top: 30px;">
                    <el-button type="primary" @click="nextStep" :disabled="!selectedFunction">下一步</el-button>
                </div>
            </div>

            <!-- 步骤2: 参数设置 -->
            <div v-if="activeStep === 1" class="step-content">
                <div v-if="selectedFunction === 'scan'">
                    <h3>扫描目录设置</h3>
                    <el-input v-model="scanDir" placeholder="请输入要扫描的目录" style="width: 500px;"></el-input>
                    <!--div style="margin-top: 10px;">
                        <el-checkbox v-model="includeSubdirs">包含子目录</el-checkbox>
                    </div-->
                    <!-- 添加排除目录配置 -->
                    <div style="margin-top: 15px;">
                        <el-input 
                            v-model="excludeDirs" 
                            placeholder="请输入要排除的目录，多个目录用逗号分隔，支持通配符(*)" 
                            style="width: 500px;"
                        ></el-input>
                        <div style="margin-top: 5px; color: #909399; font-size: 12px;">
                            示例: *cache*, *node_modules*, /tmp/*, D:/system*
                        </div>
                    </div>
                    <div style="text-align: center; margin-top: 30px;">
                        <el-button @click="prevStep">上一步</el-button>
                        <el-button type="primary" @click="executeFunction" :loading="isProcessing"
                            :disabled="(selectedFunction === 'scan' && !scanDir) || 
                                    (selectedFunction === 'organize' && !outputDir) ||
                                    (selectedFunction === 'duplicate' && duplicates.length  === 0 && !isProcessing)">
                            {{ selectedFunction === 'duplicate' ? '删除选中文件' : '执行' }}
                        </el-button>
                    </div>
                </div>

                <div v-if="selectedFunction === 'organize'">
                    <h3>文件整理设置</h3>
                    <el-input v-model="excludeDirs" placeholder="请输入包含目录,如*/new/*" style="width: 500px;"></el-input>
                    <el-input v-model="outputDir" placeholder="请输入输出目录" style="width: 500px;"></el-input>
                    <!-- div style="margin-top: 10px;">
                        <el-radio-group v-model="organizeFormat">
                            <el-radio label="year_month">按年/年月 (推荐)</el-radio>
                            <el-radio label="year">仅按年</el-radio>
                        </el-radio-group>
                    </div -->
                    <div style="text-align: center; margin-top: 30px;">
                        <el-button @click="prevStep">上一步</el-button>
                        <el-button type="primary" @click="executeFunction" :loading="isProcessing"
                            :disabled="(selectedFunction === 'scan' && !scanDir) || 
                                    (selectedFunction === 'organize' && !outputDir) ||
                                    (selectedFunction === 'duplicate' && duplicates.length  === 0 && !isProcessing)">
                            {{ selectedFunction === 'duplicate' ? '删除选中文件' : '执行' }}
                        </el-button>
                    </div>
                </div>

                <div v-if="selectedFunction === 'duplicate'">
                    <h3>重复文件查找设置</h3>
                    <el-radio-group v-model="duplicateType">
                        <el-radio label="hash">按文件内容哈希值 (精确)</el-radio>
                        <el-radio label="filename">按文件名 (模糊)</el-radio>
                    </el-radio-group>
                    <el-tooltip content="控制返回的重复文件组数量，范围1-100" placement="top">
                        <el-input 
                            v-model="duplicateLimit" 
                            type="number" 
                            min="1" 
                            max="100" 
                            placeholder="请输入返回结果数量限制(默认12)" 
                            style="width: 300px;">
                            <template slot="prepend">结果数量限制</template>
                        </el-input>
                    </el-tooltip>
                    <div style="margin-top: 20px;">
                        <el-button type="primary" @click="findDuplicates">查找重复文件</el-button>
                    </div>
                    <div style="text-align: center; margin-top: 30px;">
                        <el-button @click="prevStep">上一步</el-button>
                        <el-button type="primary" @click="executeFunction" :loading="isProcessing"
                            :disabled="(selectedFunction === 'scan' && !scanDir) || 
                                    (selectedFunction === 'organize' && !outputDir) ||
                                    (selectedFunction === 'duplicate' && duplicates.length  === 0 && !isProcessing)">
                            {{ selectedFunction === 'duplicate' ? '删除选中文件' : '执行' }}
                        </el-button>
                    </div>

                    <div v-if="duplicates.length  > 0" style="margin-top: 20px; max-height: 80vh; overflow-y: auto;">
                        <el-card v-for="(group, index) in duplicates" :key="index" class="group-card">
                            <div slot="header">
                                <h4>
                                    <i class="el-icon-picture-outline" v-if="group.files[0].file_type  === 'image'"></i>
                                    <i class="el-icon-video-play" v-else-if="group.files[0].file_type  === 'video'"></i>
                                    {{ group.group_name }} (共 {{ group.files.length }} 个文件)
                                </h4>
                            </div>
                            <div>
                                <div v-for="(file, fIndex) in group.files" :key="fIndex" class="file-item">
                                    <el-checkbox v-model="file.selected"></el-checkbox>
                                    <el-tag :class="'quality-tag el-tag--' + qualityClass(file.quality)">{{ file.quality }}</el-tag>
                                    <!-- 媒体预览区域 -->
                                    <div v-if="file.file_type  === 'image'" style="margin: 0 10px;">
                                        <el-image :src="'/api/media/' + file.id+'?width=100'" class="image-preview"
                                            :class="{ 'selected': file.selected  }"
                                            @click="showMediaPreview('image', file.id)" :alt="file.path"
                                            :title="file.path" lazy>
                                    </div>
                                    <div v-else-if="file.file_type  === 'video'" style="margin: 0 10px;">
                                        <!--video :src="'/api/media/' + file.id" class="video-preview"
                                            :class="{ 'selected': file.selected  }"
                                            @click="showMediaPreview('video', file.id)" :title="file.path" muted
                                            preload="metadata">
                                            视频预览
                                        </video-->
                                        <el-image :src="'/api/media/' + file.id+'?width=100'" class="image-preview"
                                        :class="{ 'selected': file.selected  }"
                                        @click="showMediaPreview('video', file.id)" :alt="file.path"
                                        :title="file.path" lazy>
                                    </div>
                                    <div class="file-path" :title="file.path">
                                        <span class="file-path-text">
                                        <i class="el-icon-picture-outline file-type-icon"
                                            v-if="file.file_type  === 'image'"></i>
                                        <i class="el-icon-video-play file-type-icon"
                                            v-else-if="file.file_type  === 'video'"></i>
                                            {{ file.path }}
                                        </span>
                                    </div>
                                </div>
                            </div>
                        </el-card>
                    </div>
                </div>
            </div>

            <!-- 步骤3: 执行结果 -->
            <div v-if="activeStep === 2" class="step-content">
                <div v-if="result.status  === 'success'">
                    <el-alert title="操作成功" type="success" :description="result.message" show-icon>
                    </el-alert>

                    <div v-if="selectedFunction === 'duplicate'">
                        <h4>已删除 {{ result.count }} 个重复文件</h4>
                    </div>
                </div>

                <div v-if="result.status  === 'error'">
                    <el-alert title="操作失败" type="error" :description="result.message" show-icon>
                    </el-alert>
                </div>

                <div v-if="result.status  === 'pending'">
                    <el-alert title="处理中" type="info" :description="progressMessage" show-icon>
                    </el-alert>
                    <el-progress :text-inside="true" :stroke-width="20" :percentage="progressValue" status="success">
                    </el-progress>
                    <div style="text-align: center; margin-top: 10px;">
                        <small>{{ progressMessage }}</small>
                    </div>
                </div>

                <div style="text-align: center; margin-top: 30px;">
                    <el-button @click="resetProcess">返回首页</el-button>
                </div>
            </div>
        </el-card>

        <!-- 通用媒体预览弹窗 -->
        <div id="mediaPreviewModal" class="media-preview-modal">
            <div class="media-preview-content">
                <span class="close-preview"
                    @click="closePreview">&times;</span>
                <!-- 图片预览 -->
                <img id="previewImage" class="preview-media" src="" alt="图片预览" style="display: none;">
                <!-- 视频预览 -->
                <video id="previewVideo" class="preview-media" controls style="display: none;">
                    您的浏览器不支持视频播放
                </video>
            </div>
        </div>
    </div>

    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    activeStep: 0,
                    selectedFunction: '',
                    scanDir: '',
                    includeSubdirs: true,
                    outputDir: '',
                    organizeFormat: 'year_month',
                    duplicateType: 'hash',
                    duplicateLimit: 12,
                    duplicates: [],
                    result: {
                        status: '',
                        message: '',
                        count: 0
                    },
                    progressValue: 0,
                    progressMessage: '',
                    progressTimer: null,
                    isProcessing: false,
                    excludeDirs: '',  // 排除目录字符串，多个用逗号分隔
                };
            },
            methods: {
                nextStep() {
                    this.activeStep++;
                },
                prevStep() {
                    this.activeStep--;
                },
                resetProcess() {
                    // 清除定时器
                    if (this.progressTimer) {
                        clearInterval(this.progressTimer);
                        this.progressTimer = null;
                    }
                    this.activeStep = 0;
                    this.selectedFunction = '';
                    this.result = { status: '', message: '', count: 0 };
                    this.duplicates = [];
                    this.progressValue = 0;
                    this.progressMessage = '';
                    this.isProcessing = false;
                },
                qualityClass(quality) {
                    switch (quality) {
                        case '高': return 'success';
                        case '中': return 'warning';
                        case '低': return 'danger';
                        default: return '';
                    }
                },
                findDuplicates() {
                    this.isProcessing = true;
                    const limit = this.duplicateLimit && this.duplicateLimit > 0 ? this.duplicateLimit : 12;
                    axios.get(`/api/duplicates?type=${this.duplicateType}&limit=${limit}`)
                        .then(response => {
                            this.duplicates = response.data;
                            // 确保每个文件都有file_type属性
                            this.duplicates.forEach(group => {
                                group.files.forEach(file => {
                                    if (!file.file_type) {
                                        // 从路径推断文件类型
                                        const ext = file.path.split('.').pop().toLowerCase();
                                        const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff'];
                                        const videoExts = ['mp4', 'mov', 'avi', 'mkv', 'flv', 'wmv', 'mpeg', 'mpg'];

                                        if (imageExts.includes(ext)) {
                                            file.file_type = 'image';
                                        } else if (videoExts.includes(ext)) {
                                            file.file_type = 'video';
                                        } else {
                                            file.file_type = 'other';
                                        }
                                    }
                                });
                            });
                            this.isProcessing = false;
                        })
                        .catch(error => {
                            this.$message.error(' 获取重复文件失败: ' + error.message);
                            this.isProcessing = false;
                        });
                },
                closePreview() {
                    document.getElementById('mediaPreviewModal').classList.remove('active')
                    const videoPreview = document.getElementById('previewVideo');
                    videoPreview.pause()
                },
                showMediaPreview(mediaType, mediaId) {
                    // 获取模态框元素
                    const modal = document.getElementById('mediaPreviewModal');
                    const imgPreview = document.getElementById('previewImage');
                    const videoPreview = document.getElementById('previewVideo');

                    // 根据媒体类型显示对应的预览元素
                    if (mediaType === 'image') {
                        imgPreview.src = `/api/media/${mediaId}?t=${new Date().getTime()}`;
                        imgPreview.style.display = 'block';
                        videoPreview.style.display = 'none';
                    } else if (mediaType === 'video') {
                        videoPreview.src = `/api/media/${mediaId}?t=${new Date().getTime()}`;
                        videoPreview.style.display = 'block';
                        imgPreview.style.display = 'none';
                        // 视频加载完成后播放
                        videoPreview.onloadedmetadata = function () {
                            videoPreview.play().catch(e => console.log(' 自动播放失败:', e));
                        };
                    }

                    // 显示模态框
                    modal.classList.add('active');
                },
                startProgressPolling() {
                    // 清除已有定时器
                    if (this.progressTimer) {
                        clearInterval(this.progressTimer);
                    }

                    // 创建新定时器，每秒查询一次进度
                    this.progressTimer = setInterval(() => {
                        axios.get('/api/progress')
                            .then(response => {
                                const progress = response.data;
                                this.progressValue = progress.current;
                                this.progressMessage = progress.message;

                                // 检查操作是否完成
                                if (progress.status === 'completed' || progress.status === 'error') {
                                    clearInterval(this.progressTimer);
                                    this.progressTimer = null;
                                    this.isProcessing = false;

                                    this.result = {
                                        status: progress.status === 'completed' ? 'success' : 'error',
                                        message: progress.message,
                                        count: 0
                                    };

                                    // 如果是删除操作，重新获取重复文件列表
                                    if (this.selectedFunction === 'duplicate' && progress.status === 'completed') {
                                        this.activeStep--
                                        this.findDuplicates();
                                    }
                                }
                            })
                            .catch(error => {
                                console.error(' 获取进度失败:', error);
                                this.isProcessing = false;
                            });
                    }, 2000);  // 每秒查询一次
                },
                executeFunction() {
                    this.result = { status: 'pending', message: '', count: 0 };
                    this.progressValue = 0;
                    this.progressMessage = '准备开始...';
                    this.isProcessing = true;

                    if (this.selectedFunction === 'scan') {
                        axios.post('/api/scan', { input_dir: this.scanDir, exclude_dirs: this.excludeDirs })
                            .then(response => {
                                // 后端会通过进度API返回结果，这里不需要处理
                                this.actionRespond()
                            })
                            .catch(error => {
                                this.result = {
                                    status: 'error',
                                    message: '扫描失败: ' + error.message,
                                    count: 0
                                };
                                this.isProcessing = false;
                                if (this.progressTimer) {
                                    clearInterval(this.progressTimer);
                                    this.progressTimer = null;
                                }
                            });
                    }
                    else if (this.selectedFunction === 'organize') {
                        axios.post('/api/organize', { output_dir: this.outputDir, includes: this.excludeDirs })
                            .then(response => {
                                // 后端会通过进度API返回结果，这里不需要处理
                                this.actionRespond()
                            })
                            .catch(error => {
                                this.result = {
                                    status: 'error',
                                    message: '整理失败: ' + error.message,
                                    count: 0
                                };
                                this.isProcessing = false;
                                if (this.progressTimer) {
                                    clearInterval(this.progressTimer);
                                    this.progressTimer = null;
                                }
                            });
                    }
                    else if (this.selectedFunction === 'duplicate') {
                        // 收集选中的文件ID
                        let selectedIds = [];
                        this.duplicates.forEach(group => {
                            group.files.forEach(file => {
                                if (file.selected) {
                                    selectedIds.push(file.id);
                                }
                            });
                        });

                        axios.post('/api/delete', { ids: selectedIds })
                            .then(response => {
                                // 后端会通过进度API返回结果，这里不需要处理
                                this.actionRespond()
                            })
                            .catch(error => {
                                this.result = {
                                    status: 'error',
                                    message: '删除失败: ' + error.message,
                                    count: 0
                                };
                                this.isProcessing = false;
                                if (this.progressTimer) {
                                    clearInterval(this.progressTimer);
                                    this.progressTimer = null;
                                }
                            });
                    }
                },
                actionRespond() {
                    this.nextStep()
                    // 启动进度轮询
                    this.startProgressPolling();
                },
            },
            beforeDestroy() {
                // 组件销毁前清除定时器
                if (this.progressTimer) {
                    clearInterval(this.progressTimer);
                }
            }
        });
    </script>
</body>

</html>